package frontend.symtable;

import frontend.symtable.symbol.Symbol;
import frontend.symtable.symbol.VarSymbol;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 树形符号表
 * 管理符号的插入、查找和作用域的嵌套关系
 *
 * @author 杨凯伟
 * @date 2024/10/16
 */
public class SymbolTable {
    static Integer scopeNum = 0;
    private SymbolTable preTable = null;//父作用域的符号表
    private Integer scope;
    private final List<SymbolTable> nextTables = new ArrayList<>();//子作用域的符号表列表
    private final Map<String, Symbol> symbolMap = new LinkedHashMap<>();//当前作用域中的符号信息，键为符号名，值为符号对象

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Symbol> entry : symbolMap.entrySet()) {
            sb.append(this.scope).append(" ").append(entry.getKey()).append(" ").append(entry.getValue().getIdentType()).append("\n");
        }
        for (SymbolTable nextTable : nextTables) {
            if (nextTable != null)
                sb.append(nextTable.toString().toCharArray());
        }
        return sb.toString();
    }

    public SymbolTable() {
        scopeNum += 1;
        this.scope = scopeNum;
    }

    public boolean contains(String name) {
        return symbolMap.containsKey(name);
    }

    /**
     * 向当前作用域符号表增加符号
     *
     * @param symbol 新增符号
     */
    public void insertSymbol(Symbol symbol) {
        symbolMap.put(symbol.ident, symbol);
        symbol.table = this;
    }

    /**
     * 根据名称从当前作用域符号表获取符号信息
     *
     * @param ident 符号名称
     * @return {@code Symbol}
     */
    public Symbol getSymbol(String ident) {
        if (symbolMap.containsKey(ident)) {
            return symbolMap.get(ident);
        }
        if (preTable != null) {
            return preTable.getSymbol(ident);
        } else {
            return null;
        }
    }

    /**
     * 创建一个下一层作用域符号表
     *
     * @return {@code SymbolTable}
     */
    public SymbolTable createSubTable() {
        SymbolTable subTable = new SymbolTable();
        nextTables.add(subTable);
        subTable.preTable = this;
        subTable.scope = scopeNum;
        return subTable;
    }

    public SymbolTable getPreTable() {
        return preTable;
    }

    public List<SymbolTable> getNextTables() {
        return nextTables;
    }
}
